#include "Base/Logger.h"
#include "Base/Timestamp.h"
#include "Base/CurrectThread.h"
#include "Base/FixedBuffer.h"
#include <algorithm>
#include <cstring>
#include <cstdio>

namespace toym
{

namespace detail
{

std::size_t convert(short s, char* buf);
std::size_t convert(int i, char* buf);
std::size_t convert(long l, char* buf);
std::size_t convert(long long ll, char* buf);
std::size_t convert(unsigned short s, char* buf);
std::size_t convert(unsigned int i, char* buf);
std::size_t convert(unsigned long l, char* buf);
std::size_t convert(unsigned long long ll, char* buf);
std::string convert(short s);
std::string convert(int i);
std::string convert(long l);
std::string convert(long long ll);
std::string convert(unsigned short s);
std::string convert(unsigned int i);
std::string convert(unsigned long l);
std::string convert(unsigned long long ll);

char digits[] = {'9', '8', '7', '6', '5', '4', '3', '2', '1', '0', 
                 '1', '2', '3', '4', '5' ,'6', '7', '8', '9'};
char* zero = digits + 9;


// 返回字符串长度（不包括'\0'）
std::size_t convert(long long ll, char* buf) {

    long long value = ll;
    char* p = buf;
    do {
        long long idx = value%10;
        value /= 10;
        *(p++) = zero[idx];
    }while (value != 0LL);

    if (ll < 0LL) {
        *(p++) = '-';
    }

    *p = '\0';

    std::reverse(buf, p);

    return p - buf;
}

std::size_t convert(long l, char* buf) {
    return convert(static_cast<long long>(l), buf);
}

std::size_t convert(int l, char* buf) {
    return convert(static_cast<long long>(l), buf);
}

std::size_t convert(short l, char* buf) {
    return convert(static_cast<long long>(l), buf);
}

std::size_t convert(unsigned long long ull, char* buf) {
    unsigned long long value = ull;
    char* p = buf;
    do {
        unsigned long long idx = value%10;
        value /= 10;
        *(p++) = zero[idx];
    }while (value != 0);
    *p = '\0';
    std::reverse(buf, p);
    return p - buf;
}

std::size_t convert(unsigned long l, char* buf) {
    return convert(static_cast<unsigned long long>(l), buf);
}

std::size_t convert(unsigned int l, char* buf) {
    return convert(static_cast<unsigned long long>(l), buf);
}

std::size_t convert(unsigned short l, char* buf) {
    return convert(static_cast<unsigned long long>(l), buf);
}

std::string convert(short s) {
    char buf[64];
    convert(s, buf);
    return std::string(buf);
}
std::string convert(int i) {
    char buf[64];
    convert(i, buf);
    return std::string(buf);
}
std::string convert(long l) {
    char buf[64];
    convert(l, buf);
    return std::string(buf);
}
std::string convert(long long ll) {
    char buf[64];
    convert(ll, buf);
    return std::string(buf);
}
std::string convert(unsigned short us) {
    char buf[64];
    convert(us, buf);
    return std::string(buf);
}
std::string convert(unsigned int ui) {
    char buf[64];
    convert(ui, buf);
    return std::string(buf);
}
std::string convert(unsigned long ul) {
    char buf[64];
    convert(ul, buf);
    return std::string(buf);
}
std::string convert(unsigned long long ull) {
    char buf[64];
    convert(ull, buf);
    return std::string(buf);
}

} // namespace detail


const char* LogPuter::s_logLevelStr[static_cast<unsigned long>(LogLevel::nLogLevel)] = {
    "DEBUG",
    "INFO",
    "WARNING",
    "ERROR",
    "CRITICAL"
};

LogPuter::LogLevel LogPuter::s_level = LogPuter::LogLevel::DEBUG;

OutputFunc_t LogPuter::s_output = LogPuter::defaultOutput;
FlushFunc_t LogPuter::s_flush = LogPuter::defaultFlush;


LogPuter::LogPuter(const char* fileName, int line, LogLevel level)
    :m_fileName(fileName),
     m_reserveSize(std::strlen(fileName)+10+4+60),
     m_line(line),
     m_level(level),
     m_buf()
{
    char buf[24];
    std::string now = Timestamp::now().toString();
    detail::convert(CurrectThread::getTid(), buf);

    m_buf.append(now.c_str());
    m_buf.append(' ');
    m_buf.append(buf);
    m_buf.append(' ');
    m_buf.append(s_logLevelStr[static_cast<int>(m_level)]);
    m_buf.append(' ');

}

LogPuter::~LogPuter() {
    char buf[32];
    char* tmp;

    if (CurrectThread::getErrno() != 0) {
        m_buf.append(" errno str: ");
        tmp = ::strerror_r(CurrectThread::getErrno(), buf, sizeof(buf));
        m_buf.append(tmp);
        m_buf.append(", errno: ");
        detail::convert(CurrectThread::getErrno(), buf);
        m_buf.append(buf);
    }

    m_buf.append(' ');
    m_buf.append(m_fileName);
    m_buf.append(':');
    detail::convert(m_line, buf);
    m_buf.append(buf);
    m_buf.append('\n');
    s_output(m_buf.data(), m_buf.used());
}

// characters
LogPuter& LogPuter::operator<<(char c) {
    if (m_buf.availableSize() > m_reserveSize)
        m_buf.append(c);
    return *this;
}
LogPuter& LogPuter::operator<<(unsigned char uc) {
    return operator<<(static_cast<char>(uc));
}


// numbers
LogPuter& LogPuter::operator<<(long long num) {
    char buf[64];
    std::size_t sz = detail::convert(num, buf);
    if (m_buf.availableSize() >= m_reserveSize + sz) {
        m_buf.append(buf);
    }
    return *this;
}

LogPuter& LogPuter::operator<<(unsigned long long num) {
    char buf[64];
    std::size_t sz = detail::convert(num, buf);
    if (m_buf.availableSize() >= m_reserveSize + sz) {
        m_buf.append(buf);
    }
    return *this;
}

LogPuter& LogPuter::operator<<(long num) {
    return operator<<(static_cast<long long>(num));
}

LogPuter& LogPuter::operator<<(int num) {
    return operator<<(static_cast<long long>(num));
}

LogPuter& LogPuter::operator<<(short num) {
    return operator<<(static_cast<long long>(num));
}

LogPuter& LogPuter::operator<<(unsigned long num) {
    return operator<<(static_cast<unsigned long long>(num));
}

LogPuter& LogPuter::operator<<(unsigned int num) {
    return operator<<(static_cast<unsigned long long>(num));
}

LogPuter& LogPuter::operator<<(unsigned short num) {
    return operator<<(static_cast<unsigned long long>(num));
}


// strings
LogPuter& LogPuter::operator<<(const char* s) {
    if (m_buf.availableSize() >= m_reserveSize + ::strlen(s)) {
        m_buf.append(s);
    }
    return *this;
}

LogPuter& LogPuter::operator<<(char* s) {
    return operator<<(static_cast<const char*>(s));
}

LogPuter& LogPuter::operator<<(const unsigned char* s) {
    return operator<<(reinterpret_cast<const char*>(s));
}

LogPuter& LogPuter::operator<<(unsigned char* s) {
    return operator<<(reinterpret_cast<const char*>(s));
}

LogPuter& LogPuter::operator<<(const std::string& s) {
    if (m_buf.availableSize() >= m_reserveSize + s.size()) {
        m_buf.append(s.c_str(), s.size());
    }
    return *this;
}

template<std::size_t SZ>
LogPuter& LogPuter::operator<<(const char msg[SZ]) {
    ::printf("here\n");
    if (m_buf.availableSize() >= m_reserveSize + SZ - 1) {
        m_buf.append(msg);
    }
    return *this;
}

// template<typename T>
// LogPuter& LogPuter::operator<<(T* pointer) {
//     char buf[32];
//     int sz = std::snprintf(buf, sizeof (buf), "%p", pointer);
//     if (sz > 0 && m_buf.availableSize() >= m_reserveSize + sz) {
//         m_buf.append(buf, static_cast<std::size_t>(sz));
//     }
//     return *this;
// }

void LogPuter::defaultOutput(const char* buf, std::size_t sz) {
    std::fwrite(buf, 1, sz, stdout);
}

void LogPuter::defaultFlush() {
    std::fflush(stdout);
}

} // namespace toym
